/*
 * Copyright (C) 2012 Rothens
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package rpg;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import rpg.Tiles.RoadTile;
import rpg.Tiles.Tile;
import rpg.entities.Entity;
import rpg.entities.Sprite;
import rpg.entities.factories.SpriteFactory;
import rpg.entities.properties.CreatureProperty;
import rpg.gfx.Bitmap;
import rpg.gfx.light.LightSource;
import rpg.util.LineReader;
import rpg.util.BinaryMapHandler;

/**
 * rothens.tumblr.com
 *
 * @author Rothens
 */
public class Maps {

    /**
     * This is a class that contains all tile layers.
     */
    public class MapTile {

        private Tile landscape = null;
        private RoadTile road = null;
        private Tile layer1 = null;
        private Tile layer2 = null;

        public MapTile() {
        }

        public MapTile(Tile landscape) {
            this.landscape = landscape;
        }

        public void setRoad(RoadTile road) {
            this.road = road;
        }

        public RoadTile getRoad() {
            return road;
        }

        public void setLandcape(Tile landscape) {
            this.landscape = landscape;
        }

        public void setLayer1(Tile layer1) {
            this.layer1 = layer1;
        }

        public void setLayer2(Tile layer2) {
            this.layer2 = layer2;
        }

        public Tile getLandscape() {
            return landscape;
        }

        public Tile getLayer1() {
            return layer1;
        }

        public Tile getLayer2() {
            return layer2;
        }

        public boolean isPassable() {

            return (road != null)
                    || ((landscape == null || landscape.passable)
                    && (layer1 == null || layer1.passable)
                    && (layer2 == null || layer2.passable));
        }

        /**
         * Checks if light can pass through this tile.
         *
         * @return
         */
        public boolean isTransparent() {
            return (landscape != null && landscape.transparent) && (layer1 == null || layer1.transparent) && (layer2 == null || layer2.transparent);
        }
    }
    Tiles tileset = new Tiles();
    BinaryMapHandler bmap = new BinaryMapHandler();

    public class Map {

        int width;
        int height;
        public MapTile[] tiles;
        public List<LightSource> lightSources = new ArrayList<LightSource>();
        Location spawn = null;

        public Map(int width, int height, String mapdata, String layer1data, String layer2data, String roadData, String entityData) {
            this.width = width;
            this.height = height;
            processMap(mapdata);
            processLayer1(layer1data);
            processLayer2(layer2data);
            processRoads(roadData);
            processEntities(entityData);

        }

        public int getWidth() {
            return width;
        }

        public int getHeight() {
            return height;
        }

        private void processMap(String data) {
            int i = 0;
            tiles = new MapTile[width * height];
            for (char c : data.toCharArray()) {
                tiles[i] = new MapTile(tileset.getTile(c));
                i++;
                if (i == width * height) {
                    break;
                }
            }
            if (i < width * height - 1) {
                for (; i < width * height; i++) {
                    tiles[i] = new MapTile(tileset.getTile('~'));
                }
            }
        }

        public Bitmap getCorner(int i) {
            Bitmap ret = new Bitmap(64, 64);
            Tile act = tiles[i].landscape;
            Tile[] neighbors = new Tile[4];
            int sum = 0;
            for (int j = 0; j < 4; j++) {
                neighbors[j] = getTile(i, j, this).landscape;
                if (neighbors[j].name == act.name || neighbors[j].name == '~') {

                    sum += Math.pow(2, j);
                }
            }


            switch (sum) {
                case 15:
                    ret = ((Tiles.MultiTile) tiles[i].landscape).bitmaps[4];
                    return ret;
                case 14:
                    ret.draw(getTile(i, UP, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[1], 0, 0);
                    return ret;
                case 13:
                    ret.draw(getTile(i, LEFT, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[3], 0, 0);
                    return ret;
                case 12:
                    ret.draw(getTile(i, LEFT, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[0], 0, 0);
                    return ret;
                case 11:
                    ret.draw(getTile(i, DOWN, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[7], 0, 0);
                    return ret;
                case 9:
                    ret.draw(getTile(i, DOWN, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[6], 0, 0);
                    return ret;
                case 6:
                    ret.draw(getTile(i, UP, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[2], 0, 0);
                    return ret;
                case 7:
                    ret.draw(getTile(i, RIGHT, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[5], 0, 0);
                    return ret;
                case 3:
                    ret.draw(getTile(i, DOWN, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[8], 0, 0);
                    return ret;

                default:
                    ret = ((Tiles.MultiTile) tiles[i].landscape).bitmaps[4];
                    break;
            }





            return ret;
        }

        public Bitmap getCornerRoad(int i) {
            Bitmap ret = new Bitmap(64, 64);
            Tile act = tiles[i].landscape;
            Tile[] neighbors = new Tile[4];
            int sum = 0;
            for (int j = 0; j < 4; j++) {
                neighbors[j] = getTile(i, j, this).landscape;
                if (neighbors[j].name == act.name || neighbors[j].name == '~') {

                    sum += Math.pow(2, j);
                }
            }


            switch (sum) {
                case 15:
                    ret = ((Tiles.MultiTile) tiles[i].landscape).bitmaps[4];
                    return ret;
                case 14:
                    ret.draw(getTile(i, UP, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[1], 0, 0);
                    return ret;
                case 13:
                    ret.draw(getTile(i, LEFT, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[3], 0, 0);
                    return ret;
                case 12:
                    ret.draw(getTile(i, LEFT, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[0], 0, 0);
                    return ret;
                case 11:
                    ret.draw(getTile(i, DOWN, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[7], 0, 0);
                    return ret;
                case 9:
                    ret.draw(getTile(i, DOWN, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[6], 0, 0);
                    return ret;
                case 6:
                    ret.draw(getTile(i, UP, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[2], 0, 0);
                    return ret;
                case 7:
                    ret.draw(getTile(i, RIGHT, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[5], 0, 0);
                    return ret;
                case 3:
                    ret.draw(getTile(i, DOWN, this).landscape.bitmap, 0, 0);
                    ret.draw(((Tiles.MultiTile) tiles[i].landscape).bitmaps[8], 0, 0);
                    return ret;

                default:
                    ret = ((Tiles.MultiTile) tiles[i].landscape).bitmaps[4];
                    break;
            }





            return ret;
        }

        private void processLayer1(String layer1data) {
            int i = 0;

            for (char c : layer1data.toCharArray()) {
                if (c == '.') {
                    i++;
                    continue;
                }
                if (c == '*' && spawn == null) {
                    spawn = new Location(i, this);
                }
                tiles[i].setLayer1(tileset.getLayer1(c));
                i++;
                if (i == width * height) {
                    break;
                }
            }
            if (i < width * height - 1) {
                for (; i < width * height; i++) {
                    tiles[i].setLayer1(null);
                }
            }
        }

        private void processLayer2(String layer2data) {
            int i = 0;

            for (char c : layer2data.toCharArray()) {
                if (c == '.') {
                    i++;
                    continue;
                }
                tiles[i].setLayer2(tileset.getLayer2(c));
                i++;
                if (i == width * height) {
                    break;
                }
            }
            if (i < width * height - 1) {
                for (; i < width * height; i++) {
                    tiles[i].setLayer2(null);
                }
            }
        }

        private void processRoads(String roadData) {
            int i = 0;

            for (char c : roadData.toCharArray()) {
                if (c == '.') {
                    i++;
                    continue;
                }
                try {
                    tiles[i].setRoad((RoadTile) tileset.getRoad(c).clone());
                } catch (Exception ex) {
                }
                i++;
                if (i == width * height) {
                    break;
                }
            }
            if (i < width * height - 1) {
                for (; i < width * height; i++) {
                    tiles[i].setRoad(null);
                }
            }

            for (i = 0; i < width * height; i++) {
                RoadTile act = tiles[i].getRoad();
                if (act == null) {
                    continue;
                }
                int val = 0;
                for (int j = 0; j < 4; j++) {
                    RoadTile toCheck = getRoadTile(i, j, this);
                    if (toCheck == null) {
                        continue;
                    }
                    if (act.getName() == toCheck.getName()) {
                        val += Math.pow(2, j);
                    }
                }
                act.setState(val);

            }

        }

        public boolean isPassableEntity(int tileLoc) {

            /**
             * Jelenleg minden Sprite passable
             */
            /* 
             for (SpriteFactory e : SpriteFactory.getEntites()) {
             Location l = e.getProp().getLocation();
             if (l.getMapCoordinate() == tileLoc) {
             if (((Property)(((Sprite)e).getProp().)).) {
             return false;
             }
             }
             }*/
            return true;
        }

        public List<Entity> getEntity(int tileLoc) {
            List<Entity> ret = new ArrayList<Entity>();
            for (SpriteFactory e : SpriteFactory.getEntites()) {
                Location l = e.getProp().getLocation();
                if (l.getMapCoordinate(l) == tileLoc) {
                    ret.add(e);
                }
            }
            return ret;
        }

        private void processEntities(String entityData) {
            int i = 0;
            for (char c : entityData.toCharArray()) {
                if (c == '.') {
                    i++;
                    continue;
                }
                SpriteFactory.newInstance(c, this, i);
                i++;
                if (i == width * height) {
                    break;
                }
            }

        }

        private RoadTile getRoadTile(int actual, int dir, Map map) {
            switch (dir) {
                case UP:
                    if (actual - map.width < 0) {
                        return null;
                    }
                    return map.tiles[actual - map.width].getRoad();
                case LEFT:
                    if (actual % map.width == 0) {
                        return null;
                    }
                    return map.tiles[actual - 1].getRoad();
                case DOWN:
                    if (actual + map.width >= map.width * map.height) {
                        return null;
                    }
                    return map.tiles[actual + map.width].getRoad();
                case RIGHT:
                    if (actual % map.width == map.width - 1) {
                        return null;
                    }
                    return map.tiles[actual + 1].getRoad();
            }
            return null;
        }
    }
    public static final int UP = 0; //1
    public static final int LEFT = 1; //2
    public static final int DOWN = 2; //4
    public static final int RIGHT = 3; //8

    public MapTile getTile(int actual, int dir, Map map) {
        switch (dir) {
            case UP:
                if (actual - map.width < 0) {
                    return map.tiles[actual];
                }
                return map.tiles[actual - map.width];
            case LEFT:
                if (actual % map.width == 0) {
                    return map.tiles[actual];
                }
                return map.tiles[actual - 1];
            case DOWN:
                if (actual + map.width >= map.width * map.height) {
                    return map.tiles[actual];
                }
                return map.tiles[actual + map.width];
            case RIGHT:
                if (actual % map.width == map.width - 1) {
                    return map.tiles[actual];
                }
                return map.tiles[actual + 1];
        }
        return null;
    }

    public Maps() {
        tileset.loadTiles();
    }

    /*public Map loadMap(String name) {
     Map ret = null;
     LineReader lr = new LineReader(name);
     int w = Integer.parseInt(lr.next());
     if (!lr.hasNext()) {
     return null;
     }
     int h = Integer.parseInt(lr.next());
     String data = "";
     while (lr.hasNext()) {
     String next = lr.next();
     if (next.equals("--")) {
     break;
     }
     data += next;
     }
     String layer1 = "";
     while (lr.hasNext()) {
     String next = lr.next();
     if (next.equals("--")) {
     break;
     }
     layer1 += next;
     }
     String layer2 = "";
     while (lr.hasNext()) {
     String next = lr.next();
     if (next.equals("--")) {
     break;
     }
     layer2 += next;
     }
     String road = "";
     while (lr.hasNext()) {
     String next = lr.next();
     if (next.equals("--")) {
     break;
     }
     road += next;
     }
     String entityData = "";
     while (lr.hasNext()) {
     String next = lr.next();
     if (next.equals("--")) {
     break;
     }
     entityData += next;
     }
     ret = new Map(w, h, data, layer1, layer2, road, entityData);
    
     return ret;
     }*/
    public Map loadMap(File file1) {
        //String name = file1.getPath();
        LineReader lr = new LineReader(file1);
        int w = Integer.parseInt(lr.next());
        if (!lr.hasNext()) {
            return null;
        }
        int h = Integer.parseInt(lr.next());
        String data = "";
        while (lr.hasNext()) {
            String next = lr.next();
            if (next.equals("--")) {
                break;
            }
            data += next;
        }
        String layer1 = "";
        while (lr.hasNext()) {
            String next = lr.next();
            if (next.equals("--")) {
                break;
            }
            layer1 += next;
        }
        String layer2 = "";
        while (lr.hasNext()) {
            String next = lr.next();
            if (next.equals("--")) {
                break;
            }
            layer2 += next;
        }
        String road = "";
        while (lr.hasNext()) {
            String next = lr.next();
            if (next.equals("--")) {
                break;
            }
            road += next;
        }
        String entityData = "";
        while (lr.hasNext()) {
            String next = lr.next();
            if (next.equals("--")) {
                break;
            }
            entityData += next;
        }

        return new Map(w, h, data, layer1, layer2, road, entityData);
    }

    public Map loadBinaryMap(String name) {
        return bmap.loadBinaryMap(name);
    }
}
